package com.icongyou.enterprise.talent_pool.service.impl;

import com.icongyou.enterprise.talent_pool.entity.TalentSearchHistory;
import com.icongyou.enterprise.talent_pool.entity.vo.TalentSearchHistoryListVO;
import com.icongyou.enterprise.talent_pool.entity.vo.TalentSearchHistoryVO;
import com.icongyou.enterprise.talent_pool.mapper.TalentSearchHistoryMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class TalentSearchHistoryServiceImplTest {

    @Mock
    private TalentSearchHistoryMapper talentSearchHistoryMapper;

    @InjectMocks
    private TalentSearchHistoryServiceImpl talentSearchHistoryService;

    private String userId;
    private String demandText;
    private TalentSearchHistory existingHistory;
    private TalentSearchHistory newHistory;

    @BeforeEach
    void setUp() {
        userId = "test-user-123";
        demandText = "Java Developer";

        existingHistory = new TalentSearchHistory();
        existingHistory.setId("existing-id-123");
        existingHistory.setUserId(userId);
        existingHistory.setDemandText(demandText);
        existingHistory.setSearchCount(5);
        existingHistory.setLastSearchTime(LocalDateTime.now().minusDays(1));

        newHistory = new TalentSearchHistory();
        newHistory.setId(UUID.randomUUID().toString());
        newHistory.setUserId(userId);
        newHistory.setDemandText(demandText);
        newHistory.setSearchCount(1);
        newHistory.setLastSearchTime(LocalDateTime.now());
    }

    @Test
    void saveSearchHistory_WhenExistingHistoryExists_ShouldUpdateSearchCount() {
        // Arrange
        when(talentSearchHistoryMapper.findByUserIdAndDemandText(userId, demandText.trim()))
                .thenReturn(Optional.of(existingHistory));

        // Act
        talentSearchHistoryService.saveSearchHistory(userId, demandText);

        // Assert
        verify(talentSearchHistoryMapper).updateSearchCount(eq(userId), eq(demandText.trim()), any(LocalDateTime.class));
        verify(talentSearchHistoryMapper, never()).insert(any(TalentSearchHistory.class));
    }

    @Test
    void saveSearchHistory_WhenNewHistoryAndUnderLimit_ShouldInsertNewRecord() {
        // Arrange
        when(talentSearchHistoryMapper.findByUserIdAndDemandText(userId, demandText.trim()))
                .thenReturn(Optional.empty());
        when(talentSearchHistoryMapper.countByUserId(userId)).thenReturn(10);

        // Act
        talentSearchHistoryService.saveSearchHistory(userId, demandText);

        // Assert
        verify(talentSearchHistoryMapper).insert(any(TalentSearchHistory.class));
        verify(talentSearchHistoryMapper, never()).deleteOldestByUserId(anyString(), anyInt());
    }

    @Test
    void saveSearchHistory_WhenNewHistoryAndExceedsLimit_ShouldDeleteOldestAndInsert() {
        // Arrange
        when(talentSearchHistoryMapper.findByUserIdAndDemandText(userId, demandText.trim()))
                .thenReturn(Optional.empty());
        when(talentSearchHistoryMapper.countByUserId(userId)).thenReturn(25);

        // Act
        talentSearchHistoryService.saveSearchHistory(userId, demandText);

        // Assert
        verify(talentSearchHistoryMapper).deleteOldestByUserId(userId, 6);
        verify(talentSearchHistoryMapper).insert(any(TalentSearchHistory.class));
    }

    @Test
    void saveSearchHistory_WhenUserIdIsNull_ShouldDoNothing() {
        // Act
        talentSearchHistoryService.saveSearchHistory(null, demandText);

        // Assert
        verify(talentSearchHistoryMapper, never()).findByUserIdAndDemandText(anyString(), anyString());
        verify(talentSearchHistoryMapper, never()).insert(any(TalentSearchHistory.class));
    }

    @Test
    void saveSearchHistory_WhenDemandTextIsNull_ShouldDoNothing() {
        // Act
        talentSearchHistoryService.saveSearchHistory(userId, null);

        // Assert
        verify(talentSearchHistoryMapper, never()).findByUserIdAndDemandText(anyString(), anyString());
        verify(talentSearchHistoryMapper, never()).insert(any(TalentSearchHistory.class));
    }

    @Test
    void saveSearchHistory_WhenDemandTextIsEmpty_ShouldDoNothing() {
        // Act
        talentSearchHistoryService.saveSearchHistory(userId, "   ");

        // Assert
        verify(talentSearchHistoryMapper, never()).findByUserIdAndDemandText(anyString(), anyString());
        verify(talentSearchHistoryMapper, never()).insert(any(TalentSearchHistory.class));
    }

    @Test
    void getSearchHistory_ShouldReturnConvertedVOList() {
        // Arrange
        List<TalentSearchHistory> histories = Arrays.asList(existingHistory, newHistory);
        when(talentSearchHistoryMapper.findByUserIdOrderByLastSearchTimeDesc(userId, 20))
                .thenReturn(histories);

        // Act
        List<TalentSearchHistoryVO> result = talentSearchHistoryService.getSearchHistory(userId);

        // Assert
        assertNotNull(result);
        assertEquals(2, result.size());
        verify(talentSearchHistoryMapper).findByUserIdOrderByLastSearchTimeDesc(userId, 20);
    }

    @Test
    void getSearchHistoryPage_ShouldReturnPaginatedResult() {
        // Arrange
        List<TalentSearchHistory> histories = Arrays.asList(existingHistory, newHistory);
        when(talentSearchHistoryMapper.findByUserIdOrderByLastSearchTimeDesc(userId, 20))
                .thenReturn(histories);

        // Act
        TalentSearchHistoryListVO result = talentSearchHistoryService.getSearchHistoryPage(userId, 1, 10);

        // Assert
        assertNotNull(result);
        assertEquals(1, result.getCurrentPage());
        assertEquals(10, result.getPageSize());
        assertEquals(2, result.getTotalCount());
        assertFalse(result.getHasMore());
        assertEquals(2, result.getHistoryList().size());
    }

    @Test
    void getSearchHistoryPage_WithNullParameters_ShouldUseDefaults() {
        // Arrange
        List<TalentSearchHistory> histories = Arrays.asList(existingHistory, newHistory);
        when(talentSearchHistoryMapper.findByUserIdOrderByLastSearchTimeDesc(userId, 20))
                .thenReturn(histories);

        // Act
        TalentSearchHistoryListVO result = talentSearchHistoryService.getSearchHistoryPage(userId, null, null);

        // Assert
        assertNotNull(result);
        assertEquals(1, result.getCurrentPage());
        assertEquals(10, result.getPageSize());
    }

    @Test
    void deleteSearchHistory_SuccessfulDeletion_ShouldReturnTrue() {
        // Arrange
        String historyId = "history-123";
        when(talentSearchHistoryMapper.deleteById(historyId)).thenReturn(1);

        // Act
        boolean result = talentSearchHistoryService.deleteSearchHistory(userId, historyId);

        // Assert
        assertTrue(result);
        verify(talentSearchHistoryMapper).deleteById(historyId);
    }

    @Test
    void deleteSearchHistory_FailedDeletion_ShouldReturnFalse() {
        // Arrange
        String historyId = "history-123";
        when(talentSearchHistoryMapper.deleteById(historyId)).thenReturn(0);

        // Act
        boolean result = talentSearchHistoryService.deleteSearchHistory(userId, historyId);

        // Assert
        assertFalse(result);
    }

    @Test
    void deleteSearchHistory_ExceptionThrown_ShouldReturnFalse() {
        // Arrange
        String historyId = "history-123";
        when(talentSearchHistoryMapper.deleteById(historyId)).thenThrow(new RuntimeException("DB error"));

        // Act
        boolean result = talentSearchHistoryService.deleteSearchHistory(userId, historyId);

        // Assert
        assertFalse(result);
    }

    @Test
    void clearSearchHistory_SuccessfulClear_ShouldReturnTrue() {
        // Arrange
        List<TalentSearchHistory> histories = Arrays.asList(existingHistory, newHistory);
        when(talentSearchHistoryMapper.findByUserIdOrderByLastSearchTimeDesc(userId, Integer.MAX_VALUE))
                .thenReturn(histories);

        // Act
        boolean result = talentSearchHistoryService.clearSearchHistory(userId);

        // Assert
        assertTrue(result);
        verify(talentSearchHistoryMapper, times(2)).deleteById(anyString());
    }

    @Test
    void clearSearchHistory_ExceptionThrown_ShouldReturnFalse() {
        // Arrange
        when(talentSearchHistoryMapper.findByUserIdOrderByLastSearchTimeDesc(userId, Integer.MAX_VALUE))
                .thenThrow(new RuntimeException("DB error"));

        // Act
        boolean result = talentSearchHistoryService.clearSearchHistory(userId);

        // Assert
        assertFalse(result);
    }
}